#include "comp_mgr.h"
#include "pool.h"

#include <stdlib.h>

namespace HW
{
namespace ComponentManager
{
	CompRegData regData[MAX_COMPONENT_TYPES];
	bool isTypeRegistred[MAX_COMPONENT_TYPES];
	uint8_t regedCompNum;
	UpdateChain mainUpdateChain;

	uint8_t* pool;
	Memory::Pool::Roster roster;

	Memory::Pool::PoolContainer poolContainer[MAX_COMPONENT_TYPES];

	//////////////////////////////////////////////////////////////////////////////
	// Global component manager routines
	bool RegisterComponent ( const CompRegData& compRegData )
	{
		if ( compRegData.typeID >= MAX_COMPONENT_TYPES )
			return false;

		if ( isTypeRegistred[compRegData.typeID] )
			return false;
		
		isTypeRegistred[compRegData.typeID] = true;
		regedCompNum++;

		for ( uint32_t i = 0 ; i < MAX_UPDATE_STAGES_PER_COMP ; ++i )
		{
			if ( compRegData.callbackCompUpdate[i] != NULL )
				++mainUpdateChain.count;
		}
		
		poolContainer[compRegData.typeID].elementCount = compRegData.maxInstances;
		poolContainer[compRegData.typeID].elementSize = compRegData.dataSize;
		poolContainer[compRegData.typeID].isUsed = true;

		regData[compRegData.typeID] = compRegData;

		return true;
	}

	void PreInit ( void )
	{
		regedCompNum = 0;
		mainUpdateChain.count = 0;
		memset ( isTypeRegistred , 0 , MAX_COMPONENT_TYPES * sizeof(bool) );
		for ( uint8_t i = 0 ; i < MAX_COMPONENT_TYPES ; ++i )
			poolContainer[i].isUsed = false;
	}

	int UpdateElementComparator ( const void * elem1, const void * elem2 )
	{
		return ( ((UpdateElement*)elem1)->order - ((UpdateElement*)elem2)->order );
	}

	bool ConstructMainUpdateChain ( void )
	{
		mainUpdateChain.element = (UpdateElement*)malloc(sizeof(UpdateElement)*mainUpdateChain.count);
		if ( mainUpdateChain.element == NULL )
			return false;

		for ( HW_COMPONENT_TYPE i = 0 , currIndex = 0 ; i < MAX_COMPONENT_TYPES ; ++i )
		{
			if ( !isTypeRegistred[i] )
				continue;
			for ( uint8_t j = 0 ; j < MAX_UPDATE_STAGES_PER_COMP && currIndex < mainUpdateChain.count ; ++j )
			{
				if ( regData[i].callbackCompUpdate[j] != NULL )
				{
					mainUpdateChain.element[currIndex].updateFunc = regData[i].callbackCompUpdate[j];
					mainUpdateChain.element[currIndex].count = regData[i].maxInstances;
					mainUpdateChain.element[currIndex].order = regData[i].updateOrder[j];
					mainUpdateChain.element[currIndex].poolLoc = poolContainer[i].pool;
					mainUpdateChain.element[currIndex].result = NULL;
					currIndex ++;
				}
			}
		}

		qsort ( mainUpdateChain.element , mainUpdateChain.count , sizeof(UpdateElement) , UpdateElementComparator );

		return true;
	}

	void ExtractUpdateChain ( UpdateChain& updateChain , HW_UPDATE_ORDER_TYPE first , HW_UPDATE_ORDER_TYPE last )
	{
		uint32_t firstIndex = mainUpdateChain.count;
		uint32_t lastIndex = 0;
		for ( uint32_t i = 0 ; i < mainUpdateChain.count ; ++i )
		{
			if ( mainUpdateChain.element[i].order >= first )
			{
				firstIndex = i;
				break;
			}
		}
		for ( uint32_t i = mainUpdateChain.count - 1 ; i >= 0  ; --i )
		{
			if ( mainUpdateChain.element[i].order < last )
			{
				lastIndex = i;
				break;
			}
		}

		if ( firstIndex > lastIndex )
		{
			updateChain.count = 0;
			return;
		}

		updateChain.element = &(mainUpdateChain.element[firstIndex]);
		updateChain.count = lastIndex - firstIndex + 1;
	}

	bool Init ( void )
	{
		if ( !Memory::Pool::CreatePool ( pool , roster , poolContainer , MAX_COMPONENT_TYPES ) )
			return false;
		Memory::Pool::InitPool ( pool , roster , poolContainer , MAX_COMPONENT_TYPES );

		if ( !ConstructMainUpdateChain ( ) )
			return false;

		return true;
	}

	void DeInit ( void )
	{
		DestroyAll ( );

		free ( pool );
		free ( roster );
		free ( mainUpdateChain.element );
	}

	void DestroyAll ( void )
	{
		for ( HW_COMPONENT_TYPE i = 0 ; i < MAX_COMPONENT_TYPES ; ++i )
		{
			if ( !isTypeRegistred[i] )
				continue;

			regData[i].callbackCompDeInit ( regData[i].maxInstances , poolContainer[i].pool , NULL );
		}
	}

	void Update ( const UpdateChain& updateChain )
	{
		for ( uint32_t i = 0 ; i < updateChain.count ; ++i )
		{
			updateChain.element[i].updateFunc ( updateChain.element[i].count , updateChain.element[i].poolLoc , updateChain.element[i].result );
		}
	}
	//////////////////////////////////////////////////////////////////////////////

	//////////////////////////////////////////////////////////////////////////////
	HW_COMPONENT_TYPE GetComponentType ( const_str name )
	{
		for ( HW_COMPONENT_TYPE i = 0 ; i < MAX_COMPONENT_TYPES ; ++i )
		{
			if ( !isTypeRegistred[i] )
				continue;
			if ( strcmp ( name , regData[i].typeName ) == 0 )
				return i;
		}

		return MAX_COMPONENT_TYPES;
	}

	uint32_t GetNumAllocated ( HW_COMPONENT_TYPE compType )
	{
		if ( !isTypeRegistred[compType] )
			return 0;
		return poolContainer[compType].rosterFreePartition;
	}

	//////////////////////////////////////////////////////////////////////////////

	//////////////////////////////////////////////////////////////////////////////
	HW_COMPONENT_ID CreateComponent ( HW_COMPONENT_TYPE compType )
	{
		if ( !isTypeRegistred[compType] )
			return NULL;
		
		HW_COMPONENT_ID newID;
		newID =  Memory::Pool::AllocateFromPool ( poolContainer[compType] );
		if ( newID == poolContainer[compType].elementCount )
			return NULL;

		newID |= (uint32_t(compType))<<RZERO_COMPONENT_TYPE;
		newID |= TYPE_GLOBAL_COMPONENT;
		
		bool initRes;
		regData[compType].callbackCompInit ( 1 , GetComponent ( newID ) , NULL , &initRes );
		if ( !initRes )
		{
			Memory::Pool::DeallocateFromPool ( poolContainer[compType] , newID&MASK_COMPONENT_PER_TYPE );
			return NULL;
		}

		return newID;

	}

	void DestroyComponent ( HW_COMPONENT_ID compID )
	{
		HW_COMPONENT_TYPE compType = GetComponentType ( compID );

		if ( !IsAllocated(compID) )
			return;

		bool initRes;
		regData[compType].callbackCompDeInit ( 1 , GetComponent ( compID ) , &initRes );
		if ( !initRes )
		{
			//Dont khow yet what to do here!
		}

		Memory::Pool::DeallocateFromPool ( poolContainer[compType] , compID&MASK_COMPONENT_PER_TYPE );
	}

	//////////////////////////////////////////////////////////////////////////////

	//////////////////////////////////////////////////////////////////////////////
	// Component instances routines
	HW_COMPONENT_ID GetComponentID ( HW_GLOBALID_TYPE objID , HW_COMPONENT_TYPE compType )
	{
		if ( !isTypeRegistred[compType] )
			return NULL;
	}

	HW_COMPONENT_ID GetComponentID ( HW_COMPONENT_TYPE compType )
	{
		if ( !isTypeRegistred[compType] )
			return NULL;
		if ( GetNumAllocated ( compType ) == 0 )
			return NULL;
		return ((uint32_t(compType))<<RZERO_COMPONENT_TYPE) | TYPE_GLOBAL_COMPONENT | poolContainer[compType].roster[0];
	}

	/*HW_COMPONENT_ID GetComponentID ( HW_COMPONENT comp )
	{
		ptrdiff_t diff = ((uint8_t*)comp) - pool;

		if ( diff < 0 )
			return NULL;
		
		return NULL;
	}*/


	HW_COMPONENT GetComponent ( HW_COMPONENT_ID compID )
	{
		if ( !IsAllocated ( compID ) )
			return NULL;
		HW_COMPONENT_TYPE compType = GetComponentType(compID);
		return poolContainer[compType].pool + (poolContainer[compType].elementSize*compID&MASK_COMPONENT_PER_TYPE);
	}

	HW_COMPONENT_TYPE GetComponentType ( HW_COMPONENT_ID compID )
	{
		return (uint8_t)( (compID & MASK_COMPONENT_TYPE) >> RZERO_COMPONENT_TYPE );
	}

	HW_GLOBALID_TYPE GetHost ( HW_COMPONENT_ID compID )
	{
		//TODO:
		return 0;
	}

	bool IsValid ( HW_COMPONENT_ID compID )
	{
		if ( 
			( (compID&MASK_GLOBAL_TYPE) != TYPE_GLOBAL_COMPONENT ) ||
			( !isTypeRegistred[GetComponentType(compID)] ) ||
			( (compID&MASK_COMPONENT_PER_TYPE) >= regData[GetComponentType(compID)].maxInstances )
			)
			return false;
		else
			return true;
	}

	bool IsAllocated ( HW_COMPONENT_ID compID )
	{
		if ( !IsValid ( compID ) )
			return false;
		if ( Memory::Pool::IsAllocated ( poolContainer[GetComponentType(compID)] , compID&MASK_COMPONENT_PER_TYPE ) )
			return true;
		return false;
	}

	//////////////////////////////////////////////////////////////////////////////
}
}